/**
 *  Copyright (C) 2008-2013  Telosys project org. ( http://www.telosys.org/ )
 *
 *  Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *          http://www.gnu.org/licenses/lgpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.telosys.persistence.impl;

import java.util.List;

import org.telosys.common.dataset.DataSet;
import org.telosys.persistence.EntityInfo;
import org.telosys.persistence.PersistenceManager;
import org.telosys.persistence.PersistenceManagerFactory;
import org.telosys.persistence.PersistenceTransaction;
import org.telosys.persistence.Query;
import org.telosys.persistence.TelosysPersistence;
import org.telosys.persistence.dao.SqlRequests;
import org.telosys.persistence.database.DatabaseInfo;
import org.telosys.persistence.database.DatabaseManager;
import org.telosys.persistence.sql.Criteria;
import org.telosys.persistence.sql.SqlQuery;

public class PersistenceManagerProxy implements PersistenceManager
{

	private final PersistenceManagerFactory persistenceManagerFactory ;
	
    //-------------------------------------------------------------------------------------------------------------------
	/**
	 * Constructor for default database
	 */
	public PersistenceManagerProxy() 
	{
		super();
		this.persistenceManagerFactory = TelosysPersistence.getPersistenceManagerFactory() ;
	}
	
	/**
	 * Constructor for a specific database id
	 * @param databaseId
	 */
	public PersistenceManagerProxy(int databaseId) 
	{
		super();
		this.persistenceManagerFactory = TelosysPersistence.getPersistenceManagerFactory(databaseId) ;
	}

    //-------------------------------------------------------------------------------------------------------------------
    // PROXY SPECIFIC METHODS 
    //-------------------------------------------------------------------------------------------------------------------
	
	private PersistenceManagerImpl getTransactionalPersistenceManagerFromThread() {
		
		return CurrentPersistenceManager.get();
	}

	private PersistenceManagerImpl createNewPersistenceManager() {
		PersistenceManager pm = persistenceManagerFactory.createPersistenceManager() ;
		if ( pm instanceof PersistenceManagerImpl ) {
			return (PersistenceManagerImpl) pm ;
		}
		else {
			throw new IllegalStateException("PersistenceManager created is not an instance of 'PersistenceManagerImpl'");
		}
	}
	
	private PersistenceManagerImpl getPersistenceManager() {
		PersistenceManagerImpl pm = getTransactionalPersistenceManagerFromThread() ;
		if ( pm != null) {
			// We are in a transaction => use the current transactional PersistenceManager instance 
			// ( it must be open and not in "auto-commit" mode )
			if ( pm.isOpen() == false ) {
				throw new IllegalStateException("The current transactional PersistenceManager is closed.");
			}
			if ( pm.isAutoCommitMode() ) {
				throw new IllegalStateException("The current transactional PersistenceManager is in AUTO-COMMIT mode.");
			}
			
			return pm;
		}
		else {
			// We are not in a transaction => create a new PersistenceManager instance 
			pm = createNewPersistenceManager();
			pm.setType(PersistenceManagerImpl.TYPE_TEMPORARY);
			return pm ;
		}
	}
	
	private void closePersistenceManagerIfTemporary(PersistenceManagerImpl pm) {
		if ( pm.getType() == PersistenceManagerImpl.TYPE_TEMPORARY ) {
			pm.close();
		}
	}
	
    //-------------------------------------------------------------------------------------------------------------------
    // SPECIFIC IMPLEMENTATIONS FOR PROXY 
    //-------------------------------------------------------------------------------------------------------------------
	public int getInstanceId()
	{
		PersistenceManager pm = getTransactionalPersistenceManagerFromThread() ;
		if ( pm != null) {
			// Proxy with a concrete instance in the current thread
			return pm.getInstanceId();
		}
		else {
			// Proxy without a concrete instance => return 0
			return 0 ;
		}
	}
	
	// @Override
	public void close()
	{
		// Cannot close a Proxy 
		throw new IllegalStateException("Not allowed. Cannot close a PersistenceManager proxy");
	}

	// @Override
	public boolean isOpen()
	{
		PersistenceManager pm = getTransactionalPersistenceManagerFromThread() ;
		if ( pm != null) {
			return pm.isOpen();
		}
		else {
			return true ; // By default a PersistenceManager PROXY is always considered as "open" 
		}
	}
	
	// @Override
	public boolean isAutoCommitMode()
	{
		PersistenceManager pm = getTransactionalPersistenceManagerFromThread() ;
		if ( pm != null) {
			return pm.isAutoCommitMode(); // False expected for a transactional PersistenceManager
		}
		else {
			return true ; // By default a PersistenceManager is in AUTO-COMMIT mode 
		}
	}

	// @Override
	public PersistenceTransaction getTransaction() 
	{
		// Cannot manage transactions manually with a Proxy 
		throw new IllegalStateException("Not allowed. Cannot manage transaction with a PersistenceManager proxy");
	}    
	
	// @Override
	public DatabaseInfo getDatabaseInfo() 
	{
		return DatabaseManager.getDatabaseInfo( persistenceManagerFactory.getDatabaseId() ) ;
	}
	
	public <T> EntityInfo getEntityInfo(Class<T> entityClass) {
		return getPersistenceManager().getEntityInfo(entityClass) ;
	}

	
	// @Override
	public <T> SqlRequests<T> getSqlRequests(Class<T> entityClass)
	{
		return getPersistenceManager().getSqlRequests(entityClass) ;
	}
	
    //-------------------------------------------------------------------------------------------------------------------
    // METHODS IMPLEMENTATIONS BY DELEGATION 
    //-------------------------------------------------------------------------------------------------------------------
	
    //-------------------------------------------------------------------------------------------------------------------
    // LOAD
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public boolean load(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		boolean ret = pm.load(entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
	
	// @Override
    public boolean loadForUpdate(Object entity) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		boolean ret = pm.loadForUpdate(entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

	// @Override
    public <T> T loadByKey(Class<T> entityClass, Object ... keyValues ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
    	T ret = pm.loadByKey(entityClass, keyValues);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
	// @Override
    public <T> T loadByKeyForUpdate(Class<T> entityClass, Object ... keyValues ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
    	T ret = pm.loadByKeyForUpdate(entityClass, keyValues);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

    //-------------------------------------------------------------------------------------------------------------------
    // LINKS
    //-------------------------------------------------------------------------------------------------------------------
	public <T> T loadLink(Class<T> referencedObjectClass, Object entity) {
		PersistenceManagerImpl pm = getPersistenceManager();
    	T ret = pm.loadLink(referencedObjectClass, entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
    public <T> T loadLink(Class<T> referencedObjectClass, Object entity, String linkName ) {
		PersistenceManagerImpl pm = getPersistenceManager();
    	T ret = pm.loadLink(referencedObjectClass, entity, linkName);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }    

	public <T> List<T> loadLinks(Class<T> referencedObjectClass, Object entity) {
		PersistenceManagerImpl pm = getPersistenceManager();
    	List<T> ret = pm.loadLinks(referencedObjectClass, entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
	public <T> List<T> loadLinks(Class<T> referencedObjectClass, Object entity, String linkName ) {
		PersistenceManagerImpl pm = getPersistenceManager();
    	List<T> ret = pm.loadLinks(referencedObjectClass, entity, linkName);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
    //-------------------------------------------------------------------------------------------------------------------
    // SAVE
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public void save(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		pm.save(entity);
		closePersistenceManagerIfTemporary(pm);
	}

    //-------------------------------------------------------------------------------------------------------------------
    // DELETE
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public boolean delete(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		boolean ret = pm.delete(entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
	
    //-------------------------------------------------------------------------------------------------------------------
    // UPDATE
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public boolean update(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		boolean ret = pm.update(entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
	
    //-------------------------------------------------------------------------------------------------------------------
    // INSERT
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public void insert(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		pm.insert(entity);
		closePersistenceManagerIfTemporary(pm);
	}

	// @Override
	public long insertKeyGen(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		long ret = pm.insertKeyGen(entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
	
    //-------------------------------------------------------------------------------------------------------------------
    // EXISTS
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public boolean exists(Object entity) 
	{
		PersistenceManagerImpl pm = getPersistenceManager();
		boolean ret = pm.exists(entity);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
	}
	
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public <T> List<T> loadByQuery( Query<T> query ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		List<T> ret = pm.loadByQuery(query);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
	// @Override
    public <T> int loadByQuery( Query<T> query, List<T> list )
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.loadByQuery(query, list);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
	// @Override
    public <T> List<T> loadAll( Class<T> entityClass )
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		List<T> ret = pm.loadAll(entityClass);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
	// @Override
    public <T> List<T> loadAll( Class<T> entityClass, String orderBy )
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		List<T> ret = pm.loadAll(entityClass, orderBy);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
    //-------------------------------------------------------------------------------------------------------------------
    // INSERT LIST
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public int insert(List<? extends Object> list ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.insert(list);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

	// @Override
    public int insert(List<? extends Object> list, Class<?> entityClass) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.insert(list, entityClass);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

    //-------------------------------------------------------------------------------------------------------------------
    // UPDATE LIST
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public int update(List<? extends Object> list ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.update(list);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
	// @Override
    public int update(List<? extends Object> list, Class<?> entityClass ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.update(list, entityClass);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

    //-------------------------------------------------------------------------------------------------------------------
    // SAVE LIST
    //-------------------------------------------------------------------------------------------------------------------

    //-------------------------------------------------------------------------------------------------------------------
    // DELETE LIST
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public int delete(List<? extends Object> list ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.delete(list);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

    // @Override
    public int delete(List<? extends Object> list, Class<?> entityClass ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.delete(list, entityClass);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
	
    
    //-------------------------------------------------------------------------------------------------------------------
    // DELETE BY QUERY
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public int delete(Query<?> query) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.delete(query);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
	
    //-------------------------------------------------------------------------------------------------------------------
    // COUNT WITH QUERY
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public int count(Query<?> query) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.count(query);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
	
	// @Override
    public int countAll(Class<?> entityClass) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.countAll(entityClass);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
	

    //-------------------------------------------------------------------------------------------------------------------
    // DATASET
    //-------------------------------------------------------------------------------------------------------------------
    public int countDataSetRows( SqlQuery query) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		int ret = pm.countDataSetRows(query);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
    public DataSet loadDataSet( SqlQuery query) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		DataSet ret = pm.loadDataSet(query);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
    
    public void loadDataSet( SqlQuery query, DataSet dataset ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		pm.loadDataSet(query, dataset);
		closePersistenceManagerIfTemporary(pm);
    }
    
    //-------------------------------------------------------------------------------------------------------------------
    // QUERY ALL
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public <T> Query<T> createQueryAll( Class<T> entityClass ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		Query<T> ret = pm.createQueryAll(entityClass);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

	// @Override
    public <T> Query<T> createQueryAll( Class<T> entityClass, String sQueryEndOfSelect ) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		Query<T> ret = pm.createQueryAll(entityClass, sQueryEndOfSelect);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

    //-------------------------------------------------------------------------------------------------------------------
    // QUERY
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
    public <T> Query<T> createQuery(Class<T> entityClass, String sQueryCriteria) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		Query<T> ret = pm.createQuery(entityClass, sQueryCriteria);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

	// @Override
    public <T> Query<T> createQuery(Class<T> entityClass, String sQueryCriteria, String sQueryEndOfSelect) 
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		Query<T> ret = pm.createQuery(entityClass, sQueryCriteria, sQueryEndOfSelect);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

    //-------------------------------------------------------------------------------------------------------------------
    // QUERY WITH CRITERIA 
    //-------------------------------------------------------------------------------------------------------------------
	// @Override
	public <T> Query<T> createQuery(Class<T> entityClass, Criteria criteria)
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		Query<T> ret = pm.createQuery(entityClass, criteria);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

	// @Override
	public <T> Query<T> createQuery(Class<T> entityClass, Criteria criteria, String sQueryEndOfSelect)
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		Query<T> ret = pm.createQuery(entityClass, criteria, sQueryEndOfSelect);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

	
	public SqlQuery getNamedQuery(String name)
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		SqlQuery ret = pm.getNamedQuery(name);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }
	
    //-------------------------------------------------------------------------------------------------------------------
    // SEQUENCE
    //-------------------------------------------------------------------------------------------------------------------
	//@Override
    public long sequenceNextVal( String sequenceName )
    {
		PersistenceManagerImpl pm = getPersistenceManager();
		long ret = pm.sequenceNextVal(sequenceName);
		closePersistenceManagerIfTemporary(pm);
		return ret ;
    }

}
